import logging
from logging.handlers import TimedRotatingFileHandler
from flask import Flask, request, g
import time
import os
from datetime import datetime

app = Flask(__name__)

# 确保日志目录存在
log_dir = 'logs'
os.makedirs(log_dir, exist_ok=True)


# ================================================
# 1. 改进的日志处理器配置（修复文件名问题）
# ================================================
class DailyRotatingFileHandler(TimedRotatingFileHandler):
    """自定义文件名格式的每日轮转处理器"""

    def __init__(self, log_name, **kwargs):
        filename = os.path.join(log_dir, f"{log_name}.log")
        super().__init__(
            filename=filename,
            when='midnight',
            interval=1,
            backupCount=7,
            encoding='utf-8',
            **kwargs
        )
        self.suffix = "%Y-%m-%d"
        self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.log)?$"

    def getFilesToDelete(self):
        """重写旧文件清理逻辑，适配自定义文件名"""
        dirName, baseName = os.path.split(self.baseFilename)
        fileNames = os.listdir(dirName)
        result = []
        prefix = baseName + "."
        plen = len(prefix)
        for fileName in fileNames:
            if fileName[:plen] == prefix:
                suffix = fileName[plen:]
                if self.extMatch.match(suffix):
                    result.append(os.path.join(dirName, fileName))
        if len(result) < self.backupCount:
            result = []
        else:
            result.sort()
            result = result[:len(result) - self.backupCount]
        return result


# ================================================
# 2. 改进的日志格式化（支持结构化日志）
# ================================================
class StructuredFormatter(logging.Formatter):
    def format(self, record):
        # 转换字典类型消息为JSON字符串
        if isinstance(record.msg, dict):
            record.msg = self._dict_to_str(record.msg)
        return super().format(record)

    @staticmethod
    def _dict_to_str(log_dict):
        """将日志字典转换为易读字符串"""
        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
        items = [
            f"[{timestamp}]",
            f"{log_dict.get('method', '')}",
            f"{log_dict.get('path', '')}",
            f"status={log_dict.get('status', 0)}",
            f"ip={log_dict.get('ip', '')}",
            f"{log_dict.get('response_time', 0)}ms",
            f"ua={log_dict.get('user_agent', '')[:50]}"
        ]
        return ' '.join(items)


# ================================================
# 3. 日志初始化配置
# ================================================
def setup_logger(name, level=logging.INFO):
    """创建带自定义格式的日志记录器"""
    handler = DailyRotatingFileHandler(name)

    formatter = StructuredFormatter(
        '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)d] - %(message)s'
    )
    handler.setFormatter(formatter)

    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.addHandler(handler)
    logger.propagate = False  # 防止向上传播
    return logger


# 初始化各类型日志
access_logger = setup_logger('access', logging.INFO)
error_logger = setup_logger('error', logging.ERROR)
app_logger = setup_logger('app', logging.INFO)


# ================================================
# 4. 请求日志记录（修复访问日志不写入问题）
# ================================================
@app.before_request
def record_start_time():
    g.start_time = time.time()


@app.after_request
def log_access(response):
    try:
        response_time = round((time.time() - g.start_time) * 1000, 2)
        log_data = {
            'method': request.method,
            'path': request.path,
            'status': response.status_code,
            'ip': request.remote_addr,
            'response_time': response_time,
            'user_agent': request.user_agent.string
        }
        access_logger.info(log_data)
    except Exception as e:
        error_logger.error(f"记录访问日志失败: {str(e)}", exc_info=True)
    return response


# ================================================
# 5. 错误处理示例（验证错误日志）
# ================================================
@app.route('/error')
def trigger_error():
    try:
        1 / 0
    except Exception as e:
        error_logger.error("发生除零错误", exc_info=True)
        return "服务器错误", 500


# ================================================
# 6. 验证日志轮转（测试接口）
# ================================================
@app.route('/log-test')
def log_test():
    """测试所有日志类型的接口"""
    access_logger.info({"event": "手动访问测试", "status": "success"})
    app_logger.info("业务日志测试: 用户ID=123")
    return "查看日志文件验证效果"


# ================================================
# 7. 服务启动
# ================================================
if __name__ == '__main__':
    # 禁用Flask默认日志处理器
    app.logger.handlers.clear()
    app.run(host='0.0.0.0', port=5000, debug=False)
